package zdream.pmfield.stage.effect;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;

import zdream.pmfield.data.foe.MoveAdditionalEffects;
import zdream.pmfield.data.foe.Type;
import zdream.pmfield.stage.director.AttackContext;
import zdream.pmfield.stage.director.FixPair;
import zdream.pmfield.stage.director.HookStore;
import zdream.pmfield.stage.director.HookType;
import zdream.pmfield.stage.director.Hooks;

import static zdream.pmfield.data.foe.MoveAdditionalEffects.E_FIXED;

/**
 * @author Zdream
 * @date 2023-11-27
 * @since 0.0.1
 */
public class TypeFixedRateEffect extends AbstractEffect {
	private final List<FixedRateItem> rateItemList = new ArrayList<>();

	public TypeFixedRateEffect(MoveAdditionalEffects.EffectDescription description) {
		super(E_FIXED);

		final Map<String, ?> parsed = MoveAdditionalEffects.parse(description);
		Objects.requireNonNull(parsed);
		for (Map.Entry<String, ?> entry : parsed.entrySet()) {
			rateItemList.add(new FixedRateItem(Type.valueOf(entry.getKey()), (Integer) entry.getValue()));
		}
	}

	@Override
	public void init(Function<HookType, HookStore.HookHandlerBuilder> onHook) {
		onHook.apply(HookType.ON_CHECK_TYPE_FIXED)
				.handle(this::applyEffect)
				.buildAndSave();
	}

	private void applyEffect(Hooks.HookContext rawContext) {
		final Hooks.AttackHookContext hooks = rawContext.as();
		final AttackContext attackContext = hooks.attackContext;

		for (AttackContext.AttackContextItem item : attackContext.items) {
			for (FixPair pair : item.typeFixPairs) {
				if (!pair.isForType()) {
					continue;
				}

				final Type defenseType = pair.asType().defense;

				final FixedRateItem matchedItem = findItemByType(defenseType);
				if (matchedItem == null) {
					continue;
				}

				if (defenseType.equals(matchedItem.type)) {
					pair.fixRateLevel = matchedItem.level;
				}
			}
		}
	}

	private FixedRateItem findItemByType(Type type) {
		return rateItemList.stream()
				.filter(item -> item.type == type)
				.findFirst()
				.orElse(null);
	}

	private static class FixedRateItem {
		final Type type;
		final byte level;

		public FixedRateItem(Type type, int level) {
			this.type = Objects.requireNonNull(type);
			this.level = (byte) level;
		}
	}
}
